/*
  (C) 2005 Michael Ocean

  This program is free software; you can redistribute it and/or
  modify it under the terms of Version 2 of the GNU General Public 
  License.
  
  This library is distributed in the hope that it will be useful,
  but WITHOUT ANY WARRANTY; without even the implied warranty of
  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
  General Public License for more details.
  
  You should have received a copy of the GNU Library General Public
  License along with this library; if not, write to the Free
  Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/

package step;

import java.util.ArrayList;

import snbenchlib.Log;
import snbenchlib.Trace;
import snobject.snBoolean;
import step.exceptions.IllegalChildException;
import step.exceptions.STEPException;

public class ConditionalNode extends Node {
	
	public static final int ASLEEP = 0;
	public static final int TESTING = 1;
	public static final int PROCESSING = 2;
	public static final int DONE = 3;
	
	protected int state;
	protected int reqbranch;
	
	public static final String StateName(int state) {
		switch(state) {
		case ASLEEP:
			return "ASLEEP";
		case PROCESSING:
			return "PROCESSING";
		case TESTING:
			return "TESTING";
		case DONE:
			return "DONE";
		default:
			assert(false);
		}
		return "Unknown";
	}
	
	public ConditionalNode(String id){
		super(id);
		state = ASLEEP;
		reqbranch = -1;
	}

	public ConditionalNode() {
		super();
		state = ASLEEP;
	}
	
	protected void SetWanted(boolean v) throws EvaluationFailure{ 
		rt_wanted = v; 

		/* it's safe to set the children to be
		 * unwanted.  
		 */
		 if(!rt_wanted){
			children[0].SetWanted(v);
			children[1].SetWanted(v);
			children[2].SetWanted(v);
		}
		
		/* If wanted and done we reset the state
		 * to asleep (so it will run again) */
		if((rt_wanted)&&(state==DONE))
			state = ASLEEP;
	}	
	
	public boolean IsEnabled() {
	    if (!rt_wanted)
	    	return false;
	    if(rt_garbage)
	    	return false;
	    
		switch(state) {
			case ASLEEP:
				return true;
			case TESTING:
				return HasFreshInput(0);
			case PROCESSING:
				assert(reqbranch>0);
				return HasFreshInput(reqbranch);
			case DONE:
				return false;
		}
		assert(false);
		return false; /* should never happen */
	}

	public void Evaluate() throws EvaluationFailure {
		int oldstate = state;
		
		switch(state) {
		case ASLEEP:
			reqbranch = -1;
			children[0].ClearEvaluatedFlags();
			children[1].ClearEvaluatedFlags();
			children[2].ClearEvaluatedFlags();

			/* always set the path that's wanted last! */
			children[1].SetWanted(false);
			children[2].SetWanted(false);
			children[0].SetWanted(true);

			ClearFreshInputs();
			state = TESTING;
			break;
			
		case TESTING:
			children[0].SetWanted(false);
			if (snBoolean.TRUE.equals(GetInput(0))) {
				reqbranch = 1;
				/* always set the path that's wanted last! */
				children[2].SetWanted(false);
				children[1].SetWanted(true);

			} else if (snBoolean.FALSE.equals(GetInput(0))) {
				reqbranch = 2;
				/* always set the path that's wanted last! */
				children[1].SetWanted(false);
				children[2].SetWanted(true);
			}
			else{
				/* if the predicate is neither true nor false, this is an
				 * error in the underlying graph and we should stop evaluation
				 * here. */
				throw new EvaluationFailure(this, "Conditional node requires predicate input to be either true or false.  Value specified was " + GetInput(0));
			}
			state = PROCESSING;			
			break;
			
		case PROCESSING:
			SetValue(GetInput(reqbranch));
			children[reqbranch].SetWanted(false);
			PushResult();
			state = DONE;
			break;
		default:
			assert(false);
		}
		Log.evaluator.log(
				"COND "+ super.ID + " state advancement from " + StateName(oldstate) + " to " + StateName(state),
				Trace.DEBUG);
	}
	
	// Serializer stuff
	public String TagName() { return "cond"; }

	public String GetInfoXML() {
		return super.GetInfoXML(
			"<state>" + StateName(state) + "</state>");
	}
		//this code is added by Ray
	//	 MODIFIES: compilationErrors 
	// EFFECT: checks if a Conditional Node has exavtly 3 children, if not it adds an exception to compilationErrors
	// @Override
	public void validate(Graph g, ArrayList<STEPException> compilationErrors) {
		if (children.length != 3) {
			compilationErrors.add(new IllegalChildException("Node "
					+ this.GetID()
					+ " :A Condition Node Should have exactly 3 Children"));
		}
	}

	@Override
	public Node ShallowCopy() {
		ConditionalNode cn = new ConditionalNode(this.ID);
		Node.CopyNonRTPropertiesFromAtoB(this,cn);
		return cn;
	}

}
